﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace Flight
{
    public class Settings
    {
        #region Static
        static string basePath;
        public static string BasePath { get { return basePath; } }
        static Dictionary<Type, Func<string[], object>> parsers;

        static string only(string[] args)
        {
            if (args.Length != 1)
                throw new ArgumentException("#args != 1");
            return args[0];
        }
        static void expect(string[] args, int len)
        {
            if (args.Length != len)
                throw new ArgumentException("#args != " + len);
        }
        static Settings()
        {
            basePath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);

            parsers = new Dictionary<Type, Func<string[], object>>();
            addParser<float>(args => float.Parse(only(args)));
            addParser<double>(args => double.Parse(only(args)));
            addParser<int>(args => int.Parse(only(args)));
            addParser<string>(only);
            addParser<Vector3>(args =>
            {
                expect(args, 3);
                return new Vector3(float.Parse(args[0]), float.Parse(args[1]), float.Parse(args[2]));
            });
        }
        public static void addParser<T>(Func<string[], T> parser)
        {
            parsers[typeof(T)] = args => parser(args);
        }

        public static void setBasePath(string path)
        {
            basePath = path;
        }
        #endregion

        string path;
        Dictionary<string, string[]> settings;
        HashSet<string> used;

        public Settings(string filepath, bool relative = true)
        {
            path = filepath;
            settings = new Dictionary<string, string[]>();
            used = new HashSet<string>();
            loadSettings(System.IO.File.ReadAllLines((relative ? (basePath + "/") : "") + filepath));
        }

        void loadSettings(string[] lines)
        {
            var validLines = from line in lines
                             select line.Trim();
                             
            foreach (string line in lines.Select(line => line.Trim()).Where(line => line.Length != 0 && line[0] != '#'))
            {
                int firstBreak = line.IndexOf(' ');
                if (firstBreak < 0)
                    settings[line] = new string[0];
                else
                {
                    string prop = line.Remove(firstBreak);
                    string args = line.Substring(firstBreak).TrimStart(' ');
                    if (prop[prop.Length - 1] == ':')
                        settings[prop.Remove(prop.Length - 1)] = new string[] { args };
                    else settings[prop] = args.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                }
            }
        }

        public T get<T>(string prop)
        {
            if (!settings.ContainsKey(prop))
                throw new KeyNotFoundException("Property '" + prop + "' not found in settings.");
            used.Add(prop);
            return (T)parsers[typeof(T)](settings[prop]);
        }
        public bool has(string prop)
        {
            used.Add(prop);
            return settings.ContainsKey(prop);
        }

        public void listUnusedSettings()
        {
            string[] unused = settings.Keys.Except(used).ToArray();
            Console.WriteLine("in file " + path + ": " + unused.Length + " unused setting" + (unused.Length == 1 ? "" : "s"));
            foreach (string prop in unused)
                Console.WriteLine("  " + prop);
        }
    }
}
